package com.sishu.njrtsdms.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sishu.njrtsdms.constant.Constants;
import com.sishu.njrtsdms.domain.core.AjaxResult;
import com.sishu.njrtsdms.domain.core.LoginUser;
import com.sishu.njrtsdms.domain.core.R;
import com.sishu.njrtsdms.manager.AsyncManager;
import com.sishu.njrtsdms.manager.factory.AsyncFactory;
import com.sishu.njrtsdms.service.system.TokenService;
import com.sishu.njrtsdms.utils.ServletUtils;

import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.PrintWriter;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;

@Component
@SuppressWarnings("deprecation")
public class MyAuthenticationHandler
        implements AuthenticationFailureHandler
        , LogoutSuccessHandler
        , AccessDeniedHandler, AuthenticationEntryPoint {

    private final TokenService tokenService;

    private final ObjectMapper objectMapper;

    public MyAuthenticationHandler(TokenService tokenService,
                                   ObjectMapper objectMapper) {
        this.tokenService = tokenService;
        this.objectMapper = objectMapper;
    }

    /**
     * 认证失败处理
     *
     * @param request  that resulted in an <code>AuthenticationException</code>
     * @param response so that the user agent can begin authentication
     */
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        R<Object> result = R.fail("认证失败,请登录之后再访问资源");
        writer.write(JSONUtil.toJsonStr(result));
        writer.flush();
        writer.close();
    }

    /**
     * 权限不足时的处理
     *
     * @param request               that resulted in an <code>AccessDeniedException</code>
     * @param response              so that the user agent can be advised of the failure
     * @param accessDeniedException that caused the invocation
     */
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException,
            ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        R<Object> result = R.fail("拒绝访问,权限不足");
        writer.write(JSONUtil.toJsonStr(result));
        writer.flush();
        writer.close();
    }

    /**
     * 认证失败时的处理
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException,
            ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        R<Object> result = R.fail("认证失败,请重新登陆");
        writer.write(JSONUtil.toJsonStr(result));
        writer.flush();
        writer.close();
    }


    /**
     * 登出成功处理
     *
     * @param request        请求
     * @param response       响应
     * @param authentication 认证信息
     */
    @SneakyThrows
    @Override
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (ObjUtil.isNotNull(loginUser)) {
            String userName = loginUser.getUsername();
            // 删除用户缓存记录
            tokenService.delLoginUser(loginUser.getToken());
            // 记录用户退出日志
            AsyncManager.me().execute(
                    AsyncFactory.INSTANCE.recordLoginInfo(
                            userName,
                            Constants.LOGOUT,
                            "退出成功"));
        }
        ServletUtils.renderString(response, objectMapper.writeValueAsString(AjaxResult.success(
                "退出成功")));
    }
}


